/**
 * 
 */
package com.slink.fxfk.common.dao.imp;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate4.HibernateCallback;
import org.springframework.orm.hibernate4.HibernateTemplate;
import org.springframework.util.Assert;

import com.slink.fxfk.framework.util.CollectionUtil;
import com.slink.fxfk.framework.util.ReflectionUtils;

/**
 * 在 Service 层直接使用, 也可以扩展泛型 DAO 子类使用 T: Dao 操作的对象类型 ; PK: 主键类型
 * 
 * @author 新
 */
public class BaseHibernateDao<T, PK extends Serializable> {

	protected Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	protected HibernateTemplate hibernateTemplate;

	protected Class<T> entityClass;

	/**
	 * 用于 Dao 层子类使用的构造函数 通过子类的泛型定义取得对象类型 Class
	 * 
	 * 例如: public class UserDao extends SimpleHibernateDao<User, String>
	 */
	public BaseHibernateDao() {
		this.entityClass = ReflectionUtils.getSuperGenericType(getClass());
	}

	/**
	 * 用于省略 Dao 层, 在 Service 层直接使用通用 SimpleHibernateDao 的构造函数 在构造函数中定义对象类型 Class
	 * 
	 * @param sessionFactory
	 * @param entityClass
	 */
	public BaseHibernateDao(HibernateTemplate hibernateTemplate,
			Class<T> entityClass) {
		this.hibernateTemplate = hibernateTemplate;
		this.entityClass = entityClass;
	}

	public HibernateTemplate getHibernateTemplate() {
		return hibernateTemplate;
	}

	public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
		this.hibernateTemplate = hibernateTemplate;
	}

	/**
	 * 获取当前 Session
	 * 
	 * @return
	 */
	public Session getSession() {
		Session session = hibernateTemplate.getSessionFactory()
				.getCurrentSession();
		return session;

		// return sessionFactory.openSession();
	}

	/**
	 * 保存新增或修改的对象
	 * 
	 * @param entity
	 */
	public void save(T entity) {

		Assert.notNull(entity, "entity 不能为空");
		getSession().saveOrUpdate(entity);
		logger.debug("save entity: {}", entity);
	}

	/**
	 * 按 id 获取对象
	 * 
	 * @param id
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T get(PK id) {
		Assert.notNull(id, "id不能为空");
		return (T) getSession().get(entityClass, id);
	}

	/**
	 * 删除对象
	 * 
	 * @param entity
	 *            ： 持久化对象或"瞬态"对象
	 */
	public void delete(T entity) {
		Assert.notNull(entity, "entity 不能为空");
		getSession().delete(entity);
		logger.debug("delete entity: {}", entity);
	}

	public void delete(PK id) {
		Assert.notNull(id, "id 不能为空");
		delete(get(id));
		logger.debug("delete entity {},id is {}", entityClass.getSimpleName(),
				id);
	}

	/**
	 * 获取对象的主键名.
	 * 
	 * @return
	 */
	public String getIdName() {
		ClassMetadata meta = hibernateTemplate.getSessionFactory()
				.getClassMetadata(entityClass);
		return meta.getIdentifierPropertyName();
	}

	/**
	 * 通过 Set 将不唯一的对象列表唯一化 主要用于 HQL/Criteria 预加载关联集合形成重复记录, 又不方便使用 distinct
	 * 查询语句时: 例如: 迫切左外连接
	 * 
	 * @param <X>
	 * @param list
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <X> List<X> distinct(List list) {
		Set<X> set = new LinkedHashSet<X>(list);
		return new ArrayList<X>(set);
	}

	/**
	 * 为 Criteria 添加 distinct transformer
	 * 
	 * @param criteria
	 * @return
	 */
	public Criteria distinct(Criteria criteria) {
		criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		return criteria;
	}

	/**
	 * 为 Query 添加 distinct transformer
	 * 
	 * @param query
	 * @return
	 */
	public Query distinct(Query query) {
		query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		return query;
	}

	/**
	 * 初始化对象. 使用 load() 方法得到的仅是对象的代理, 在传到视图层前需要进行初始化
	 * 
	 * 只初始化 entity 的直接属性, 但不会初始化延迟加载的关联集合和属性 如需初始化关联属性, 可执行:
	 * Hibernate.initialize(user.getRoles());
	 * 
	 * @param entity
	 */
	public void initEntity(T entity) {
		Hibernate.initialize(entity);
	}

	public void initEntity(List<T> entityList) {
		for (T entity : entityList) {
			Hibernate.initialize(entity);
		}
	}

	/**
	 * 根据 Criterion 条件创建 Criteria
	 * 
	 * @param criterions
	 * @return
	 */
	public Criteria createCriteria(Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);

		for (Criterion c : criterions) {
			criteria.add(c);
		}

		return criteria;
	}

	/**
	 * 生产查询语句
	 * 
	 * @Title: createCriteria
	 * @Description: 根据不同条件生产SQL
	 * @param gtMap
	 * @param ltMap
	 * @param eqMap
	 * @param gteMap
	 * @param lteMap
	 * @param regexMap
	 * @param inMap
	 * @param neMap
	 * @return Criteria 查询的语句
	 * @throws
	 */
	@SuppressWarnings("rawtypes")
	public DetachedCriteria createCriteria(Map<String, Object> gtMap,
			Map<String, Object> ltMap, Map<String, Object> eqMap,
			Map<String, Object> gteMap, Map<String, Object> lteMap,
			Map<String, String> regexMap, Map<String, Collection> inMap,
			Map<String, Object> neMap) {
		DetachedCriteria criteria = DetachedCriteria.forClass(entityClass);
		Set<String> _set = null;
		List<Criterion> criterions = new ArrayList<Criterion>();
		if (gtMap != null && gtMap.size() > 0) {
			_set = gtMap.keySet();
			for (String _s : _set) {
				// criteria.add(Restrictions.gt(_s, gtMap.get(_s)));
				criterions.add(Restrictions.gt(_s, gtMap.get(_s)));
			}
		}
		if (ltMap != null && ltMap.size() > 0) {
			_set = ltMap.keySet();
			for (String _s : _set) {
				// criteria.add(Restrictions.lt(_s, ltMap.get(_s)));
				criterions.add(Restrictions.lt(_s, ltMap.get(_s)));
			}
		}
		if (eqMap != null && eqMap.size() > 0) {
			criterions.add(Restrictions.allEq(eqMap));
			// _set = eqMap.keySet();
			// for (String _s : _set) {
			// // criteria.add(Restrictions.eq(_s, eqMap.get(_s)));
			// criterions.add(Restrictions.eq(_s, eqMap.get(_s)).ignoreCase());
			// }
		}
		if (gteMap != null && gteMap.size() > 0) {
			_set = gteMap.keySet();
			for (String _s : _set) {
				// criteria.add(Restrictions.ge(_s, gteMap.get(_s)));
				criterions.add(Restrictions.ge(_s, gteMap.get(_s)));
			}
		}
		if (lteMap != null && lteMap.size() > 0) {
			_set = lteMap.keySet();
			for (String _s : _set) {
				// criteria.add(Restrictions.le(_s, lteMap.get(_s)));
				criterions.add(Restrictions.le(_s, lteMap.get(_s)));
			}
		}

		if (regexMap != null && regexMap.size() > 0) {
			_set = regexMap.keySet();
			for (String _s : _set) {
				// criteria.add(Restrictions.like(_s,
				// regexMap.get(_s)).ignoreCase());
				criterions.add(Restrictions.like(_s, regexMap.get(_s),
						MatchMode.ANYWHERE).ignoreCase());
			}
		}

		if (inMap != null && inMap.size() > 0) {
			_set = inMap.keySet();
			for (String _s : _set) {
				// criteria.add(Restrictions.in(_s, inMap.get(_s)));
				criterions.add(Restrictions.in(_s, inMap.get(_s)));
			}
		}
		if (neMap != null && neMap.size() > 0) {
			_set = neMap.keySet();
			for (String _s : _set) {
				// criteria.add(Restrictions.ne(_s, neMap.get(_s)));
				criterions.add(Restrictions.ne(_s, neMap.get(_s)));
			}
		}

		if (CollectionUtil.isNotEmpty(criterions)) {

			criteria.add(Restrictions.conjunction((Criterion[]) criterions
					.toArray()));
		}

		return criteria;
	}

	public DetachedCriteria createCriteria(Map<String, Object> eqMap) {
		return this.createCriteria(null, null, eqMap, null, null, null, null,
				null);
	}

	public DetachedCriteria createCriteria(Map<String, Object> eqMap,
			Map<String, Object> neMap) {
		return this.createCriteria(null, null, eqMap, null, null, null, null,
				neMap);
	}

	/**
	 * @Title: findCount
	 * @Description: 根据各种条件查询总数
	 * @param gtMap
	 * @param ltMap
	 * @param eqMap
	 * @param gteMap
	 * @param lteMap
	 * @param regexMap
	 * @param inMap
	 * @param neMap
	 * @return long 总数
	 * @throws
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public long findCount(Map<String, Object> gtMap, Map<String, Object> ltMap,
			Map<String, Object> eqMap, Map<String, Object> gteMap,
			Map<String, Object> lteMap, Map<String, String> regexMap,
			Map<String, Collection> inMap, Map<String, Object> neMap) {
		long count = 0;
		final DetachedCriteria detachedCriteria = this.createCriteria(gtMap,
				ltMap, eqMap, gteMap, lteMap, regexMap, inMap, neMap);
		if (null != detachedCriteria) {
			count = this.hibernateTemplate.execute(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session)
						throws HibernateException {
					Criteria criteria = detachedCriteria
							.getExecutableCriteria(session);
					return ((Long) criteria.setProjection(
							Projections.rowCount()).uniqueResult()).longValue();
				}
			});
		}
		return count;
	}

	/**
	 * 
	 * @Title: findListByPage
	 * @Description: 根据分页+条件获取对应的实体集合
	 * @param eqMap
	 * @param gtMap
	 * @param ltMap
	 * @param gteMap
	 * @param lteMap
	 * @param regexMap
	 * @param inMap
	 * @param orders
	 *            排序集合
	 * @param pageIndex
	 *            页码
	 * @param pageSize
	 *            每页条数
	 * @return List<T> 实体集合
	 * @throws
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List<T> findListByPage(Map<String, Object> eqMap,
			Map<String, Object> gtMap, Map<String, Object> ltMap,
			Map<String, Object> gteMap, Map<String, Object> lteMap,
			Map<String, String> regexMap, Map<String, Collection> inMap,
			Map<String, Object> neMap, List<Order> orders, final int pageIndex,
			final int pageSize) {
		List<T> list = null;
		final DetachedCriteria detachedCriteria = this.createCriteria(gtMap,
				ltMap, eqMap, gteMap, lteMap, regexMap, inMap, neMap);
		if (CollectionUtil.isNotEmpty(orders)) {
			for (Order order : orders) {
				detachedCriteria.addOrder(order);
			}
		}

		list = this.hibernateTemplate.execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException {
				Criteria criteria = detachedCriteria
						.getExecutableCriteria(session);
				if (pageSize > 0) {
					criteria.setFirstResult((pageIndex - 1) * pageSize)
							.setMaxResults(pageSize);
				}
				return criteria.list();
			}
		});
		return list;
	}
	
	/**
     * 
     * @Title: findListByPage
     * @Description: 多个查询条件or方式组合查询
     * @param orList
     *            or的查询条件的集合
     * @param orders排序规则
     * @param pageIndex
     *            第几页
     * @param pageSize每页多少条
     * @return List<E> 符合条件的集合
     * @throws
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
	public List<T> findListByPage(List<Order> orders, final int pageIndex,
            final int pageSize, Criterion... orList) {
        List<T> list = null;
        final DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
        		
        if (orList != null && orList.length > 0) {
        	detachedCriteria.add(Restrictions.disjunction(orList));
        }
        if (CollectionUtil.isNotEmpty(orders)) {
			for (Order order : orders) {
				detachedCriteria.addOrder(order);
			}
		}
        list = this.hibernateTemplate.execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException {
				Criteria criteria = detachedCriteria
						.getExecutableCriteria(session);
				if (pageSize > 0) {
					criteria.setFirstResult((pageIndex - 1) * pageSize)
							.setMaxResults(pageSize);
				}
				return criteria.list();
			}
		});
		return list;
    }
    
    /**
     * 
     * @Title: findList
     * @Description: 多个查询条件or方式组合查询
     * @param orList
     * @return List<E>
     * @throws
     */
    public List<T> findList(Criterion... orList) {
        return this.findListByPage(null, 0, 0, orList);
    }

	/**
	 * 按 Criteria 查询唯一对象
	 * 
	 * @param criterions
	 *            : 数量可变的 Criterion
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T findUnique(Criterion... criterions) {
		return (T) createCriteria(criterions).uniqueResult();
	}

	/**
	 * 按 Criteria 查询对象列表
	 * 
	 * @param criterions
	 *            : 数量可变的 Criterion
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> find(Criterion... criterions) {
		return createCriteria(criterions).list();
	}

	/**
	 * 获取全部
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> findAll() {
		return (List<T>) this.hibernateTemplate.find("from "
				+ this.entityClass.getSimpleName());
	}

	/**
	 * 根据detachedCriteria 获取实体数量
	 * 
	 * @param detachedCriteria
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Integer getCountbyCriteria(final DetachedCriteria detachedCriteria) {
		return this.hibernateTemplate.execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session)
					throws HibernateException {
				Criteria criteria = detachedCriteria
						.getExecutableCriteria(session);
				return ((Integer) criteria
						.setProjection(Projections.rowCount()).uniqueResult());
			}
		});
	}

	/**
	 * 根据查询 HQL 与参数列表创建 Query 对象
	 * 
	 * @param queryString
	 * @param values
	 * @return
	 */
	public Query createQuery(String queryString, Map<String, Object> values) {
		// String hql = "FROM Employee e where e.loginname = :loginname";
		Assert.hasText(queryString, "queryString不能为空");
		Query query = getSession().createQuery(queryString);

		if (values != null) {
			query.setProperties(values);
		}

		return query;
	}

	/**
	 * 根据查询 HQL 与参数列表创建 Query 对象
	 * 
	 * @param queryString
	 * @param values
	 *            : 数来那个可变的参数, 按顺序绑定
	 * @return
	 */
	public Query createQuery(String queryString, Object... values) {
		Assert.hasText(queryString, "queryString不能为空");
		Query query = getSession().createQuery(queryString);

		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}

		return query;
	}

	/**
	 * 执行 hql 进行批量修改/删除操作
	 * 
	 * @param hql
	 * @param values
	 * @return
	 */
	public int batchExecute(String hql, Map<String, Object> values) {
		return createQuery(hql, values).executeUpdate();
	}

	/**
	 * 执行 hql 进行批量修改/删除操作
	 * 
	 * @param hql
	 * @param values
	 * @return
	 */
	public int batchExecute(String hql, Object... values) {
		return createQuery(hql, values).executeUpdate();
	}

	/**
	 * 按 HQL 查询唯一对象
	 * 
	 * @param <X>
	 * @param hql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <X> X findUnique(String hql, Map<String, Object> values) {
		return (X) createQuery(hql, values).uniqueResult();
	}

	/**
	 * 按 HQL 查询唯一对象
	 * 
	 * @param <X>
	 * @param hql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <X> X findUnique(String hql, Object... values) {
		return (X) createQuery(hql, values).uniqueResult();
	}

	/**
	 * 按 HQL 查询对象列表
	 * 
	 * @param <X>
	 * @param hql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <X> List<X> find(String hql, Map<String, Object> values) {
		return createQuery(hql, values).list();
	}

	/**
	 * 按 HQL 查询对象列表
	 * 
	 * @param <X>
	 * @param hql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <X> List<X> find(String hql, Object... values) {
		return createQuery(hql, values).list();
	}

	/**
	 * 按 id 列表获取对象列表
	 * 
	 * @param ids
	 * @return
	 */
	public List<T> findByIds(List<?> ids) {
		return find(Restrictions.in(getIdName(), ids));
	}

	/**
	 * 按属性查找唯一对象, 匹配方式为相等
	 * 
	 * @param propertyName
	 * @param value
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T findUniqueBy(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName不能为空");
		Criterion criterion = Restrictions.eq(propertyName, value);
		return (T) createCriteria(criterion).uniqueResult();
	}

	/**
	 * 按属性查找对象列表, 匹配方式为相等
	 * 
	 * @param propertyName
	 * @param value
	 * @return
	 */
	public List<T> findBy(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName不能为空");

		Criterion criterion = Restrictions.eq(propertyName, value);
		return find(criterion);
	}

	/**
	 * 获取全部对象
	 * 
	 * @return
	 */
	public List<T> getAll() {
		return find();
	}

}
